<?php
/**
 * Project:     drylamp - rapid application development framework for php
 * File:        ActiveRecordTest.php
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * @copyright 2009-2010 Ilya St.
 * @author Ilya St. <provided at gmail dot com>
 * @version 1.0
 */

require_once 'PHPUnit/Framework.php';
require_once(dirname(__FILE__)."/../../autoload.php");

Conf::set( 'DB_HOST' , 'localhost' );
Conf::set( 'DB_NAME' , 'test_db' );
Conf::set( 'DB_USER' , 'test' );
Conf::set( 'DB_PASS' , 'test' );

class testtab extends ActiveRecord {
	public function getFieldsArr() {
		return $this->mFields;
	}
}


class ActiveRecordTest extends PHPUnit_Framework_TestCase {
	
	protected $doTearDown = true;
	
	protected function runQueryArr(Array $pQueryArr) {
		foreach($pQueryArr as $q) DBAdapter::$link->query($q);
    DBAdapter::$link->query("COMMIT");
	}	
	
	public function setUp() {
	  $query[] = "CREATE TABLE `testtab` (
							`tt_id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
							`col1` VARCHAR( 255 ) NULL ,
							`col2` INT( 11 ) NULL ,
							`col3` TEXT NULL ,
							`col4` FLOAT( 10, 3 ) NULL) ENGINE = innodb;";
	 
	  $query[] ="INSERT INTO `testtab` (`col1` ,`col2` ,`col3` ,`col4`) VALUES (".
						  "'col1 content', '123', 'test test test dsfsfsdfsdf', '12.4');";
				
		$this->runQueryArr($query);		
	}
	
	public function tearDown() {
		if (!$this->doTearDown) return;
		$query[] = "DROP TABLE testtab";
		$this->runQueryArr($query);
	}
	
	public function testConstruct() {
		$t = new testtab();
		$this->assertEquals($t->getTableName(), "testtab");
		$this->assertTrue(is_array($t->mFields));
		
		$this->assertEquals($t->mFields['tt_id']['Field'], 'tt_id');
		$this->assertEquals($t->mFields['tt_id']['value'], null);
		$this->assertEquals(strtolower($t->mFields['tt_id']['type']), strtolower('INT(11) UNSIGNED'));
		$this->assertFalse($t->mFields['tt_id']['null']);
		$this->assertEquals(strtolower($t->mFields['tt_id']['key']), strtolower('PRI'));
		$this->assertEquals($t->mFields['tt_id']['default'],null);
		$this->assertEquals(strtolower($t->mFields['tt_id']['extra']), strtolower("AUTO_INCREMENT"));
		$this->assertFalse($t->mFields['tt_id']['isset']);
		
		$this->assertEquals($t->mFields['col1']['Field'], 'col1');
		$this->assertEquals($t->mFields['col1']['value'], null);
		$this->assertEquals(strtolower($t->mFields['col1']['type']), strtolower('VARCHAR(255)'));
		$this->assertTrue($t->mFields['col1']['null']);
		$this->assertEquals(strtolower($t->mFields['col1']['key']), null);
		$this->assertEquals($t->mFields['col1']['default'],null);
		$this->assertEquals(strtolower($t->mFields['col1']['extra']), null);
		$this->assertFalse($t->mFields['col1']['isset']);
		
		$this->assertEquals($t->mFields['col2']['Field'], 'col2');
		$this->assertEquals($t->mFields['col2']['value'], null);
		$this->assertEquals(strtolower($t->mFields['col2']['type']), strtolower('INT(11)'));
		$this->assertTrue($t->mFields['col2']['null']);
		$this->assertEquals(strtolower($t->mFields['col2']['key']), null);
		$this->assertEquals($t->mFields['col2']['default'],null);
		$this->assertEquals(strtolower($t->mFields['col2']['extra']), null);
		$this->assertFalse($t->mFields['col2']['isset']);
		
		$this->assertEquals($t->mFields['col3']['Field'], 'col3');
		$this->assertEquals($t->mFields['col3']['value'], null);
		$this->assertEquals(strtolower($t->mFields['col3']['type']), strtolower('TEXT'));
		$this->assertTrue($t->mFields['col3']['null']);
		$this->assertEquals(strtolower($t->mFields['col3']['key']), null);
		$this->assertEquals($t->mFields['col3']['default'],null);
		$this->assertEquals(strtolower($t->mFields['col3']['extra']), null);
		$this->assertFalse($t->mFields['col3']['isset']);
		
		$this->assertEquals($t->mFields['col4']['Field'], 'col4');
		$this->assertEquals($t->mFields['col4']['value'], null);
		$this->assertEquals(strtolower($t->mFields['col4']['type']), strtolower('FLOAT(10,3)'));
		$this->assertTrue($t->mFields['col4']['null']);
		$this->assertEquals(strtolower($t->mFields['col4']['key']), null);
		$this->assertEquals($t->mFields['col4']['default'],null);
		$this->assertEquals(strtolower($t->mFields['col4']['extra']), null);
		$this->assertFalse($t->mFields['col4']['isset']);		
	}
	
	
	public function testGetFields() {
		$t = new testtab();
		$t->load(1);
		$fields = $t->getFields();
		$testArr = array('tt_id' => 1, 'col1' =>"col1 content", 'col2' => 123, 'col3'=> "test test test dsfsfsdfsdf", 'col4' => 12.4 );
		foreach($fields as $field => $val) {
			$this->assertTrue(array_key_exists($field, $testArr));
			$this->assertEquals($val, $testArr[$field]);
		}
	}
	
	
	public function testLoad() {
		$t = new testtab();
		//test successfull load:
		$this->assertFalse($t->load("testkey"));
		$this->assertTrue($t->load(1));
		$this->assertEquals($t->tt_id, 1);
		$this->assertEquals($t->col1, "col1 content");
		$this->assertEquals($t->col2, "123");
		$this->assertEquals($t->col3, 'test test test dsfsfsdfsdf');
		$this->assertEquals($t->col4, 12.4);
		unset($t);
		
		//test unsuccessfull load:
		$t = new testtab();
		$this->assertFalse($t->load(2));
	}
	
	/**
	 * @depends testLoad 
	 */
	public function testUpdate() {
		$t = new testtab();
		$t->load(1);
		$t->col1 = "new val";
		$t->col4 = 0.5;
		$t->update();
		
		$newT = new testtab();
		$newT->load(1);
		$this->assertEquals($newT->tt_id, 1);
		$this->assertEquals($newT->col1, "new val");
		$this->assertEquals($newT->col2, "123");
		$this->assertEquals($newT->col3, "test test test dsfsfsdfsdf");
		$this->assertEquals($newT->col4, 0.5);
		
		unset($t);
		
		$t = new testtab();
		$t->col1 = "col1 val";
		try {
			$t->update();
		}
		 catch(ActiveRecordException $e) {
		   //OK
		   return;	 
		 }
		$this->fail("Expected ActiveRecordException on update without setting a priKey");
	}
	
  /**
	 * @depends testLoad 
	 */
	public function testInsert() {
	  $t = new testtab();
	  $t->col1 = "col1 new val";
	  $t->col2 = 78;
	  $t->col3 = "text txt";
	  $t->col4 = 1.2;
	  $lastInsertId = $t->insert();

	  $obj = new testtab();
	  $obj->load($lastInsertId);
	  
	  $this->assertEquals($lastInsertId, 2);
	  $this->assertEquals($obj->col1 , "col1 new val");
	  $this->assertEquals($obj->col2 , 78);
	  $this->assertEquals($obj->col3 , "text txt");
	  $this->assertEquals($obj->col4 , 1.2);
	  
	  //test replace into:
	  unset($t);
	  $t = new testtab();
	  $t->tt_id = 1;
	  $t->col1 = "col1 new val";
	  $t->col2 = 78;
	  $t->col3 = "text txt";
	  $t->col4 = 1.2;
	  $caughtDBAdapterQueryFail = false;
	  try {
	  	$lastInsertId = $t->insert();
	  }
	   catch(DBAdapterQueryFail $e) {
	       	 $caughtDBAdapterQueryFail = true;
	   }
	  $this->assertTrue($caughtDBAdapterQueryFail) ;
	  $lastInsertId = $t->insert(true);

	  $this->assertEquals($lastInsertId, 1);
	  $this->assertEquals($t->col1, $obj->col1);
	  $this->assertEquals($t->col2, $obj->col2);
	  $this->assertEquals($t->col3, $obj->col3);
	  $this->assertEquals($t->col4, $obj->col4);
	}
	
	public function testDelete() {
		$t = new testtab();
	  $t->col1 = "col1 new val";
	  $t->col2 = 78;
	  $t->col3 = "text txt";
	  $t->col4 = 1.2;
	  $lastInsertId = $t->insert();
	  
	  $delObj = new testtab();
	  $delObj->tt_id = $lastInsertId;
	  $affectedRows = $delObj->delete();
	  //$this->assertEquals($affectedRows, 1); //TODO: roll this out in the next release
	  
    $lt = new testtab();
    $this->assertFalse($lt->load($lastInsertId));	  
    
    //test deletion of an object which doesn't have priKey set:
    $dt = new testtab();
    $dt->col1 = "col1 new val";
    try {
    	$dt->delete();
    }
     catch(ActiveRecordException $e) {
     	 //OK
     	 return;
     }
    $this->fail("Expected to catch ActiveRecordException on attempt to delete a dataobject without setting a priKey"); 
	}
	
	
	public function testLoadFromAssoc() {
		$t = new testtab();
		$testArr = array('tt_id' => 1, 'col1' =>"col1 content", 'col2' => 123, 'col3'=> "test test test dsfsfsdfsdf", 'col4' => 12.4 );
		$t->loadFromAssoc($testArr);
		$fields = $t->getFields();
		
		$realFields = $t->getFieldsArr();
		
		foreach($fields as $field => $val) {
			$this->assertTrue(array_key_exists($field, $testArr));
			$this->assertEquals($val, $testArr[$field]);
			$this->assertTrue($realFields[$field]['isset']);
		}
	}
	
	
	public function testAssocArrResToObjArr() {
		$arr = array(
								 array('tt_id' => 3, 'col1' => 'testcol1', 'col2' => 12345, 'col3' => 'text text text', 'col4' => 0.5 ),
								 array('tt_id' => 4, 'col1' => 'testcol4', 'col2' => 12346, 'col3' => 'text text text4', 'col4' => 0.6 ),
								 array('tt_id' => 5, 'col1' => 'testcol5', 'col2' => 12347, 'col3' => 'text text text5', 'col4' => 0.7 )
								);
    
	  $t = new testtab();
	  $objArr = $t->assocArrResToObjArr($arr); 	
	  $this->assertTrue(is_array($objArr));
    foreach($objArr as $key=>$obj) {
    	$fields = $obj->getFields();
    	foreach($fields as $field => $val) {
    		$this->assertEquals($obj->$field, $arr[$key][$field]);
    	}
    }
	}

	public function testGetPriKeyVal() {
		 $t = new testtab();
		 $t->load(1);
		 $this->assertEquals($t->getPriKeyVal(), 1);
	}
	
	public function testGetPriKey() {
		 $t = new testtab();
		 $this->assertEquals($t->getPriKey(), 'tt_id');		
	}
	
	public function testPrepareKeyVal() {
		 $t = new testtab();
		 $t->load(1);
		 $this->assertEquals($t->prepareKeyVal('tt_id'), "1" );
		 $this->assertEquals($t->prepareKeyVal('col1'), "'col1 content'" );
		 $this->assertEquals($t->prepareKeyVal('col2'), "123" );
		 $this->assertEquals($t->prepareKeyVal('col3'), "'test test test dsfsfsdfsdf'" );
		 $this->assertEquals($t->prepareKeyVal('col4'), "12.400" );
	}
	
	public function testLoadDescribeCache() {
		$t1 = new testtab();
		$t2 = new testtab();
		
		ob_start();
		var_dump($t1->getAllFields());
		$dump1 =  ob_get_contents();
		ob_end_clean();

	  ob_start();
		var_dump($t2->getAllFields());
		$dump2 =  ob_get_contents();
		ob_end_clean();
		
		$this->assertEquals($dump1, $dump2);
	}
	
	public function testEquals() {
		 $t = new testtab();
		 $t->load(1);
		 
		 $t2 = new testtab();
		 $t_fields = $t->getFields();
		 foreach( $t_fields as $key => $value) {
		 	$t2->$key = $value;
		 }
		 
		 $this->assertTrue($t->equals($t2));
		 
		 //playing with types:
		 $t2->tt_id = 1;
		 $this->assertTrue($t->equals($t2));
	}
	
	public function testGetFieldsListStr() {
		$cachePersistMethod = new ReflectionMethod('testtab', 'getFieldsListStr');
		if ($cachePersistMethod->isPublic() == false) {
			$this->markTestSkipped("getFieldsListStr method is not public (it is intentional) which makes it's evaluation imossible at this point.");
		}
		$t = new testtab();
		$this->assertEquals("testtab.tt_id, testtab.col1, testtab.col2, testtab.col3, testtab.col4", $t->getFieldsListStr());
	}
	
	public function testValidateByType() {
		$t = new testtab();
		$this->assertEquals(123, $t->validateByType('any field now', 123));
		$this->assertEquals("12\'3", $t->validateByType('any field now', "12'3"));
		$this->assertEquals("NULL", $t->validateByType('any field now', null));
		
		$this->assertEquals(123, $t->validateByType('any field now', 123, true));
		$this->assertEquals(123, $t->validateByType('any field now', '123', true));
		$this->assertEquals("'123ab'", $t->validateByType('any field now', '123ab', true));
		$this->assertEquals("'12\'3'", $t->validateByType('any field now', "12'3", true));
		$this->assertEquals("NULL", $t->validateByType('any field now', null, true));
		
		set_magic_quotes_runtime(1);
		$this->assertEquals(123, $t->validateByType('any field now', 123));
		$this->assertEquals("12\'3", $t->validateByType('any field now', "12'3"));
		$this->assertEquals("NULL", $t->validateByType('any field now', null));

		$this->assertEquals(123, $t->validateByType('any field now', 123, true));
		$this->assertEquals(123, $t->validateByType('any field now', '123', true));
		$this->assertEquals("'123ab'", $t->validateByType('any field now', '123ab', true));
		$this->assertEquals("'12\'3'", $t->validateByType('any field now', "12'3", true));
		$this->assertEquals("NULL", $t->validateByType('any field now', null, true));
		
	}
}